/*
 * anima/x86_64/cpu.cc - CPU functions for x86-64 architecture.
 * Copyright (C) 2007  Mikhail Vorozhtsov
 * See the LICENSE section of the README file for details.
 */

/* $Id$ */

#include HEADER (anima,cpu.hh)
#include HEADER (anima,thread.hh)
#include HEADER (anima,space.hh)
#include HEADER (anima,firmware.hh)
#include HEADER (anima,pdb.hh)
#include HEADER (anima,init.hh)
#include HEADER (anima,console.hh)
#include HEADER (x86_64,cpuid.hh)
#include HEADER (x86_64,regs.hh)
#include HEADER (x86_64,msrs.hh)
#include HEADER (utils,minmax.hh)
#include HEADER (utils,memory.hh)

START_NAMESPACE2 (anima,x86_64)

static const char *use_hlt = NULL;

const ::anima::cmdline::option_t cpu_t::use_hlt_option
  = ::anima::cmdline::option_t (
      "cpu:use_hlt", NULL, (const char *[]) { "yes", "no" }, &use_hlt);

bool cpu_t::use_hlt_p;

typeof (cpu_t::ioapics) cpu_t::ioapics;
cpu_t::gdt_t cpu_t::gdt;
::x86_64::sys_desc_t cpu_t::idt[256];
cpu_t::sc_handler_t cpu_t::sc_handlers[256];

u64_t cpu_t::ap_boot_code_paddr;
u64_t cpu_t::kernel_pdp_paddr;
u64_t cpu_t::tss_paddr;
u64_t cpu_t::tss_end_paddr;

volatile u32_t *cpu_t::lapic_mem;
u64_t cpu_t::apic_timer_freq;
u32_t cpu_t::apic_timer_clk_upd_tics;

extern "C" void *ap_entry_point;

extern "C" void anima_x86_64_sc_entry ();

void
cpu_t::sc_wrapper ()
{
  asm volatile (
    ".global anima_x86_64_sc_entry\n"
    ".type anima_x86_64_sc_entry, @function\n"
    "anima_x86_64_sc_entry:\n"
    "mov %%rsp, %%fs:4088\n"
    "push %%rcx\n"
    "push %%r11\n"
    "push %%r15\n"
    "push %%r14\n"
    "push %%r13\n"
    "push %%r12\n"
    "subq $8, %%rsp\n"
    "push %%r10\n"
    "push %%r9\n"
    "push %%r8\n"
    "push %%rdi\n"
    "push %%rsi\n"
    "push %%rbp\n"
    "push %%rdx\n"
    "subq $8, %%rsp\n"
    "push %%rbx\n"
    "push %%rax\n"
    "mov %%rsp, %%rdi\n"
    "mov %1, %%rcx\n"
    "andq $0xFF, %%rax\n"
    "call *(%%rcx,%%rax,8)\n"
    "pop %%rax\n"
    "pop %%rbx\n"
    "addq $8, %%rsp\n"
    "pop %%rdx\n"
    "pop %%rbp\n"
    "pop %%rsi\n"
    "pop %%rdi\n"
    "pop %%r8\n"
    "pop %%r9\n"
    "pop %%r10\n"
    "addq $8, %%rsp\n"
    "pop %%r12\n"
    "pop %%r13\n"
    "pop %%r14\n"
    "pop %%r15\n"
    "pop %%r11\n"
    "pop %%rcx\n"
    "mov %%fs:4088, %%rsp\n"
    "sysretq\n"
    :
    : "i"(SC_STACK + KB (4) - 8), "i"(sc_handlers)
  );
}

#define __ANIMA_X86_64_EVENT_HANDLER(NAME) \
  ::anima::x86_64::anima_x86_64_int_ ## NAME ## _entry

#define __ANIMA_X86_64_DEFINE_EVENT_HANDLER(NAME,ARG,PUSH,POP) \
  extern "C" void anima_x86_64_int_ ## NAME ## _entry (); \
  \
  extern "C" void \
  NAME ## _wrapper () \
  { \
    asm volatile ( \
      ".global " #NAME "_entry\n" \
      ".type " #NAME "_entry, @function\n" \
      "anima_x86_64_int_" #NAME "_entry:\n" \
      PUSH \
      "push %r15\n" \
      "push %r14\n" \
      "push %r13\n" \
      "push %r12\n" \
      "push %r11\n" \
      "push %r10\n" \
      "push %r9\n" \
      "push %r8\n" \
      "push %rdi\n" \
      "push %rsi\n" \
      "push %rbp\n" \
      "push %rdx\n" \
      "push %rcx\n" \
      "push %rbx\n" \
      "push %rax\n" \
      "mov %rsp, %rdi\n" \
      "call anima_x86_64_int_" #NAME "_handler\n" \
      "pop %rax\n" \
      "pop %rbx\n" \
      "pop %rcx\n" \
      "pop %rdx\n" \
      "pop %rbp\n" \
      "pop %rsi\n" \
      "pop %rdi\n" \
      "pop %r8\n" \
      "pop %r9\n" \
      "pop %r10\n" \
      "pop %r11\n" \
      "pop %r12\n" \
      "pop %r13\n" \
      "pop %r14\n" \
      "pop %r15\n" \
      POP \
      "iretq\n" \
    ); \
  } \
  \
  extern "C" void \
  anima_x86_64_int_ ## NAME ## _handler (cpu_t::int_frame_t *frame) \
  { \
    cpu_t:: NAME ## _handler (frame); \
  } \
  \
  void \
  cpu_t:: NAME ## _handler (cpu_t::int_frame_t *ARG)
  
#define __ANIMA_X86_64_DEFINE_INT_HANDLER(NAME,ARG) \
  __ANIMA_X86_64_DEFINE_EVENT_HANDLER ( \
      NAME, ARG, "subq $8, %rsp\n", "addq $8, %rsp\n")
#define __ANIMA_X86_64_DEFINE_ERROR_HANDLER(NAME,ARG) \
  __ANIMA_X86_64_DEFINE_EVENT_HANDLER (NAME, ARG, "", "")

__ANIMA_X86_64_DEFINE_ERROR_HANDLER (ignore, frame UNUSED)
{
  lapic::eoi ();
}

__ANIMA_X86_64_DEFINE_INT_HANDLER (apic_timer0, frame UNUSED)
{
  u64_t orig_timer_val = firmware::read_timer ();
  lapic::set_timer_icr (0xFFFFFFFF);

  while (true)
    if (firmware::read_timer () != orig_timer_val)
      break;

  while (true)
    {
      u64_t timer_val = firmware::read_timer ();
      u64_t timer_tics
        = timer_val > orig_timer_val
          ? timer_val - orig_timer_val
          : firmware::get_timer_period () - (orig_timer_val - timer_val);

      // Wait at least 500ms or half of one of the timers period.
      if (timer_tics * 2 >= firmware::get_timer_freq ()
          || timer_tics >= firmware::get_timer_period () / 2
          || lapic::get_timer_ccr () <= 0x80000000)
        break;
    }

  u64_t timer_val = firmware::read_timer ();
  u32_t apic_timer_val = lapic::get_timer_ccr ();

  lapic::set_timer_icr (0);

  u64_t timer_tics
    = timer_val > orig_timer_val
      ? timer_val - orig_timer_val
      : firmware::get_timer_period () - (orig_timer_val - timer_val);
  apic_timer_freq
    = ((0xFFFFFFFF - apic_timer_val) * firmware::get_timer_freq ())
      / timer_tics;
  apic_timer_clk_upd_tics
    = utils::min ((apic_timer_freq * firmware::get_timer_period ())
                  / (128 * 2 * firmware::get_timer_freq ()),
                  (u64_t) 0xFFFFFFFF);

  init::print_ctrl_transfer ();

  idt[INT_APIC_TIMER].set_offset (
    (u64_t) __ANIMA_X86_64_EVENT_HANDLER (apic_timer));

  lapic::eoi ();

  lapic::send (lapic::icr_t ().set_vector (INT_APIC_TIMER));
}

__ANIMA_X86_64_DEFINE_INT_HANDLER (apic_timer, frame)
{
  ::anima::cpu_t *cpu = (::anima::cpu_t *) CPU;

  if (cpu->state == ::anima::cpu_t::STATE_FRESH)
    {
      if (cpu->scheduler != NULL)
        {
          *frame = cpu->scheduler->cpu_ctx; 
          cpu->install (cpu->scheduler);
          cpu->state = ::anima::cpu_t::STATE_SCHEDULING;
        }
    }

  lapic::eoi ();
}

bool
cpu_t::has_irq_p (u32_t irq)
{
  return ioapics.lookup_key (irq) != NULL;
}

bool
cpu_t::route_irq (u32_t irq, id_t id, u8_t vector,
                  ioapic_t::trig_mode_t tm, ioapic_t::polarity_t polarity)
{
  ioapic_t *ioapic = ioapics.lookup_key (irq);

  if (!ioapic)
    return false;

  ioapic->set_re (
    irq - ioapic->get_ints_base (),
    ::x86_64::ioapic_t::re_t ().set_DST (id).set_vector (vector)
      .set_DM (ioapic_t::DM_FIXED).set_TM (tm).set_polarity (polarity)
      .set_M (false));

  return true;
}

bool
cpu_t::unroute_irq (u32_t irq)
{
  ioapic_t *ioapic = ioapics.lookup_key (irq);

  if (!ioapic)
    return false;

  ioapic->set_re (
    irq - ioapic->get_ints_base (), ::x86_64::ioapic_t::re_t ().set_M ());

  return true;
}

void *
cpu_t::map_temp (u64_t paddr)
{
  if (!init::done_p ())
    {
      mm::map_null (paddr);
      return NULL;
    }

  return NULL;
}

void
cpu_t::unmap_temp ()
{
  if (init::done_p ())
    {
    }
  else
    mm::unmap_null ();
}

END_NAMESPACE2

START_NAMESPACE (anima)

bool
cpu_t::check_feats ()
{
  ::x86_64::cpuid_ext_feats_t feats = ::x86_64::cpuid_ext_feats_t::get ();
  bool success_p = true;

  if (!feats.NX)
    {
      console::error ("CPU does not support no-execute page-protection.");
      success_p = false;
    }

  if (!feats.APIC)
    {
      console::error ("CPU does not support APIC.");
      success_p = false;
    }

  if (!feats.SYSCALL_SYSRET)
    {
      console::error ("CPU does not support SYSCALL and SYSRET instructions.");
      success_p = false;
    }

  return success_p;
}

bool
cpu_t::init ()
{
  use_hlt_p = (x86_64::use_hlt != NULL && x86_64::use_hlt[0] == 'y');

  if (ap_boot_code_paddr == 0)
    {
      console::error ("Cannot find place for AP boot code.");
      return false;
    }

  if (!mm::cp (&x86_64::ap_entry_point, ap_boot_code_paddr, KB (4)))
    {
      console::error ("Cannot copy AP boot code.");
      return false;
    }

  kernel_pdp_paddr
    = ((::x86_64::pe_t *) ::x86_64::cr3_t::get ().get_ptr ())[0].get_ptr ();

  pdb::lvl1e_t *tss_lvl1e = pdb::allocate ();
  pdb::lvl1e_t *tss_end_lvl1e = pdb::allocate ();

  if (!tss_lvl1e || !tss_end_lvl1e)
    {
      console::error ("Cannot allocate TSS.");
      return false;
    }

  tss_paddr = tss_lvl1e->entry.get_ptr ();
  tss_end_paddr = tss_end_lvl1e->entry.get_ptr ();

  // Map Local APIC.
  lapic_mem
    = (volatile u32_t *)
        mm::map (firmware::get_lapic_base (), mm::tte_t::FLAG_WRITE);

  if (!lapic_mem)
    {
      console::error ("Cannot map Local APIC.");
      return false;
    }

  // Set up GDT.
  gdt.kernel_code.set_type (::x86_64::desc_t::TYPE_CODE)
    .set_S ().set_L ().set_P ();
  gdt.user_code.set_type (::x86_64::desc_t::TYPE_CODE)
    .set_DPL (3).set_S ().set_L ().set_P ();
  gdt.kernel_data.set_type (::x86_64::desc_t::TYPE_DATA).set_S ().set_P ();
  gdt.user_data.set_type (::x86_64::desc_t::TYPE_DATA)
    .set_DPL (3).set_S ().set_P ();
  gdt.tss.set_type (::x86_64::sys_desc_t::TYPE_TSS)
    .set_base (TSS).set_limit (sizeof (::x86_64::tss_t)).set_P ();

  // Add all I/O APICs to the global list.
  {
    firmware::ioapic_t ioapic;
    void *top = NULL;

    for (const void *ptr = firmware::ioapic_t::next (NULL, ioapic);
         ptr; ptr = firmware::ioapic_t::next (ptr, ioapic))
      {
        if (ioapic.addr & 0xFFF != 0)
          {
            console::warning (
              "I/O APIC at %p is not page-aligned, skipping.", ioapic.addr);
            continue;
          }

        const pdb::region_t *region = pdb::find_region (ioapic.addr);

        if (region && region->type != pdb::region_t::TYPE_RESERVED)
          {
            console::warning (
              "I/O APIC at %p is not in reserved memory, skipping.",
              ioapic.addr);
            continue;
          }

        if (top == NULL
            || CUT ((u64_t) top, 4096)
               != CUT ((u64_t) top + sizeof (ioapic_t), 4096))
          {
            top = mm::map ();

            if (!top)
              {
                console::error ("Not enough memory to allocate I/O APICs.");
                return false;
              }
          }

        void *mem = mm::map (ioapic.addr, mm::tte_t::FLAG_WRITE);

        if (!mem)
          {
            console::error ("Not enough memory to allocate I/O APICs.");
            return false;
          }

        ioapic_t *i = (ioapic_t *) top; 
        new (i) ioapic_t (mem, ioapic.addr, ioapic.ints_base);

        if (i->ints_count () == 0)
          {
            console::warning (
              "I/O APIC at %p has zero redirect entries, skipping.",
              i->get_addr ());
            mm::unmap ((u64_t) mem);
            continue;
          }

        if (!ioapics.insert (i))
          {
            ioapic_t *i1 = ioapics.lookup_key (*i);
            console::error ("I/O APICs at %p and %p intersect.",
              i1->get_addr (), i->get_addr ());
            mm::unmap ((u64_t) mem);
            break;
          }

        top += sizeof (ioapic_t);
      }
  }

  return true;
}

cpu_t::id_t
cpu_t::get_current_id ()
{
  return (*(lapic_mem + 8) >> 24) & 0x0F;
}

bool
cpu_t::page_used_p (u64_t addr)
{
  using ::x86_64::pe_t;

  // Make sure page table provided by the boot loader won't be corrupted.
  if (!mm::initialized_p ())
    {
      const pe_t *pml4 = (const pe_t *) (::x86_64::cr3_t::get ().PML4 << 12);

      if (addr == (u64_t) pml4)
        return true;

      for (unsigned int i = 0; i < 512; ++i)
        {
          pe_t pml4e = pml4[i];

          if (!pml4e.P)
            continue;

          const pe_t *pdp = (const pe_t *) pml4e.get_ptr ();

          if (addr == (u64_t) pdp)
            return true;

          for (unsigned int j = 0; j < 512; ++j)
            {
              pe_t pdpe = pdp[j];

              if (!pdpe.P)
                continue;

              if (addr == pdpe.get_ptr ())
                return true;
            }
        }
    }

  // Find place for AP boot code.
  {
    static bool first_p = true;

    if (first_p && addr >= KB (4) && addr < MB (1))
      {
        ap_boot_code_paddr = addr;
        first_p = false;
        return true;
      }
    else
      if (addr == ap_boot_code_paddr)
        return true;
  }

  return false;
}

bool
cpu_t::init_instance ()
{
  using ::x86_64::pe_t;
  using ::x86_64::vaddr_t;

  space_t *space;

  if (init::done_p ())
    space = NULL;
  else
    space = init::get_main_space ().get ();

  pdb::lvl1e_t *pml4_lvl1e = space->allocate (pdb::entry_t::USAGE_CPU);

  if (!pml4_lvl1e)
    return false;

  pdb::lvl1e_t *pdp_lvl1e = space->allocate (pdb::entry_t::USAGE_CPU);

  if (!pdp_lvl1e)
    {
      space->deallocate (pml4_lvl1e);
      return false;
    }

  pdb::lvl1e_t *pd_lvl1e = space->allocate (pdb::entry_t::USAGE_CPU);

  if (!pd_lvl1e)
    {
      space->deallocate (pdp_lvl1e);
      space->deallocate (pml4_lvl1e);
      return false;
    }

  pdb::lvl1e_t *pt_lvl1e = space->allocate (pdb::entry_t::USAGE_CPU);

  if (!pt_lvl1e)
    {
      space->deallocate (pd_lvl1e);
      space->deallocate (pdp_lvl1e);
      space->deallocate (pml4_lvl1e);
      return false;
    }

  pdb::lvl1e_t *temp_lvl1e = space->allocate (pdb::entry_t::USAGE_CPU);

  if (!temp_lvl1e)
    {
      space->deallocate (pt_lvl1e);
      space->deallocate (pd_lvl1e);
      space->deallocate (pdp_lvl1e);
      space->deallocate (pml4_lvl1e);
      return false;
    }

  pdb::lvl1e_t *nmi_stack_lvl1e = space->allocate (pdb::entry_t::USAGE_CPU);

  if (!nmi_stack_lvl1e)
    {
      space->deallocate (temp_lvl1e);
      space->deallocate (pt_lvl1e);
      space->deallocate (pd_lvl1e);
      space->deallocate (pdp_lvl1e);
      space->deallocate (pml4_lvl1e);
      return false;
    }

  pdb::lvl1e_t *int_stack_lvl1e = space->allocate (pdb::entry_t::USAGE_CPU);

  if (!int_stack_lvl1e)
    {
      space->deallocate (nmi_stack_lvl1e);
      space->deallocate (temp_lvl1e);
      space->deallocate (pt_lvl1e);
      space->deallocate (pd_lvl1e);
      space->deallocate (pdp_lvl1e);
      space->deallocate (pml4_lvl1e);
      return false;
    }

  pe_t *pml4 = (pe_t *) map_temp (pml4_lvl1e->entry.get_ptr ());

  utils::zero (pml4, KB (4));
  pml4[0].set_ptr (kernel_pdp_paddr).set_RW ().set_P ();
  pml4[511].set_ptr (pdp_lvl1e->entry.get_ptr ()).set_NX ().set_RW ().set_P ();
  unmap_temp ();

  pe_t *pdp = (pe_t *) map_temp (pdp_lvl1e->entry.get_ptr ());

  utils::zero (pdp, KB (4));
  pdp[511].set_ptr (pd_lvl1e->entry.get_ptr ()).set_NX ().set_RW ().set_P ();
  unmap_temp ();

  pe_t *pd = (pe_t *) map_temp (pd_lvl1e->entry.get_ptr ());

  utils::zero (pd, KB (4));
  pd[511].set_ptr (pt_lvl1e->entry.get_ptr ()).set_NX ().set_RW ().set_P ();
  unmap_temp ();

  pe_t *pt = (pe_t *) map_temp (pt_lvl1e->entry.get_ptr ());

  utils::zero (pt, KB (4));

  pt[((vaddr_t) CPU).PT].set_ptr (paddr).set_NX ().set_RW ().set_P ();

  pt[((vaddr_t) PML4).PT].set_ptr (pml4_lvl1e->entry.get_ptr ())
    .set_NX ().set_RW ().set_P ();
  pt[((vaddr_t) PDP).PT].set_ptr (pdp_lvl1e->entry.get_ptr ())
    .set_NX ().set_RW ().set_P ();
  pt[((vaddr_t) PD).PT].set_ptr (pd_lvl1e->entry.get_ptr ())
    .set_NX ().set_RW ().set_P ();
  pt[((vaddr_t) PT).PT].set_ptr (pt_lvl1e->entry.get_ptr ())
    .set_NX ().set_RW ().set_P ();

  pt[((vaddr_t) TEMP).PT].set_ptr (temp_lvl1e->entry.get_ptr ())
    .set_NX ().set_RW ().set_P ();
  pt[((vaddr_t) LAPIC_BASE).PT].set_ptr (firmware::get_lapic_base ())
    .set_NX ().set_RW ().set_P ();
  pt[((vaddr_t) NMI_STACK).PT].set_ptr (nmi_stack_lvl1e->entry.get_ptr ())
    .set_NX ().set_RW ().set_P ();
  pt[((vaddr_t) INT_STACK).PT].set_ptr (int_stack_lvl1e->entry.get_ptr ())
    .set_NX ().set_RW ().set_P ();
  pt[((vaddr_t) TSS).PT].set_ptr (tss_paddr).set_NX ().set_RW ().set_P ();
  pt[((vaddr_t) (TSS + KB (12))).PT].set_ptr (tss_end_paddr)
    .set_NX ().set_RW ().set_P ();

  unmap_temp ();

  pml4_paddr = pml4_lvl1e->entry.get_ptr ();

  return true;
}

void
cpu_t::attach ()
{
  // Install page table.
  ::x86_64::cr3_t::get ().set_ptr (pml4_paddr).set ();

  // Load GDT.
  ::x86_64::set_gdt ((u64_t) &gdt, sizeof (gdt));

  asm volatile (
    // Load data segments.
    "mov %0, %%ss\n"
    "mov %0, %%ds\n"
    "mov %0, %%es\n"
    "mov %0, %%fs\n"
    "mov %0, %%gs\n"
    /*
     * We don't have far jump to 64-bit address instruction,
     * so we'll push new CS and RIP into stack and call far ret.
     */
    "push %1\n"
    "push $1f\n"
    "lretq\n"
    "1:\n"
    :
    : "r"(SELECTOR_KERNEL_DATA), "i"(SELECTOR_KERNEL_CODE)
  );

  // Load Task Register.
  {
    bool first_p = true;

    // Initialize TSS.
    if (first_p)
      {
        ::x86_64::tss_t *tss = (::x86_64::tss_t *) TSS;

        utils::zero (tss, KB (4));

        tss->ist1 = INT_STACK + KB (4);
        tss->ist2 = NMI_STACK + KB (4);
        tss->base = offsetof (::x86_64::tss_t, extra);
        tss->extra = 0xFF;

        first_p = false;
      }

    // Hack to load same TSS descriptor multiple times.
    gdt.tss.type = ::x86_64::sys_desc_t::TYPE_TSS;
    ::x86_64::set_tr (SELECTOR_TSS);
  }

  // Ignore rest of vectors reserved for processor exclusive use.
  for (u16_t i = 0; i < 32; ++i)
    {
      if (!idt[i].P)
        idt[::x86_64::INT_DE].set_type (::x86_64::sys_desc_t::TYPE_INT)
          .set_selector (SELECTOR_KERNEL_CODE).set_IST (i == 2 ? 2 : 1)
          .set_offset ((u64_t) __ANIMA_X86_64_EVENT_HANDLER (ignore))
          .set_P ();
    }

  {
    static bool first_p = true;

    idt[INT_APIC_TIMER].set_type (::x86_64::sys_desc_t::TYPE_INT)
      .set_selector (SELECTOR_KERNEL_CODE).set_IST (1)
      .set_offset ((u64_t)
         (first_p ? __ANIMA_X86_64_EVENT_HANDLER (apic_timer0)
                  : __ANIMA_X86_64_EVENT_HANDLER (apic_timer)))
      .set_P ();

    if (first_p)
      first_p = false;
  }

  // Load IDT.
  ::x86_64::set_idt ((u64_t) idt, sizeof (idt));

  // Assign syscalls-related MSRs.
  {
    ::x86_64::star_t ()
      .set_syscall_cs (SELECTOR_KERNEL_CODE)
      .set_sysret_cs (SELECTOR_USER_CODE)
      .set ();
    ::x86_64::set_msr (
      ::x86_64::MSR_LSTAR, (u64_t) anima::x86_64::anima_x86_64_sc_entry);
    ::x86_64::set_msr (::x86_64::MSR_FS_BASE, SC_STACK);
    // Disable interrups while servicing syscalls.
    ::x86_64::set_msr (
      ::x86_64::MSR_SF_MASK, ::x86_64::flags_t ().set_IF ());
  }

  // Enable Local APIC.
  lapic::set_siv (lapic::siv_t ().set_vector (INT_APIC_SI).set_E ());

  // Set up timer LVT entry.
  lapic::set_timer_lvte (lapic::lvte_t ().set_vector (INT_APIC_TIMER));

  // Set up timer.
  lapic::set_timer_dcr (lapic::TIMER_DIV_1);
  lapic::set_timer_icr (1);
}

void
cpu_t::install (thread_t *thread)
{
  using ::x86_64::pe_t;
  using ::x86_64::vaddr_t;

  ((pe_t *) PML4)[1] = thread->space->maps_pml4e;
  ((pe_t *) PML4)[2] = thread->space->first_pml4e;
  ((pe_t *) PML4)[3] = thread->space->second_pml4e;
  
  ((pe_t *) PT)[((vaddr_t) TSS).PT + 1] = thread->space->iopb0_pte;
  ((pe_t *) PT)[((vaddr_t) TSS).PT + 2] = thread->space->iopb1_pte;

  ((pe_t *) PT)[((vaddr_t) SC_STACK).PT] = thread->sc_stack_pte;
}

END_NAMESPACE

